from datetime import datetime, timedelta, UTC
from fastapi import Request, HTTPException, status, Depends
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.middleware.base import BaseHTTPMiddleware
import jwt
from app.core.config import DEBUG_MODE
from app.models.user import User, UserRole
from app.schemas.user import UserOccupied
from app.settings import AUTHENTICATE
from tortoise.exceptions import ValidationError

# JWT 配置
SECRET_KEY = AUTHENTICATE["JWT"]["SECRET_KEY"]
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = AUTHENTICATE["JWT"]["ACCESS_TOKEN_EXPIRE_MINUTES"]

# 创建 JWT Token
def create_access_token(data: dict, expires_delta: timedelta = None) -> str:
    to_encode = data.copy()
    expire = datetime.now(UTC) + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

# 创建 Refresh Token
def create_refresh_token(data: dict, expires_delta: timedelta = timedelta(days=1)) -> str:
    to_encode = data.copy()
    expire = datetime.now(UTC) + expires_delta
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

# 验证 JWT Token
def verify_jwt_token(token: str) -> dict:
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token已过期",
            headers={"WWW-Authenticate": "Bearer"},
        )
    except jwt.InvalidTokenError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的Token",
            headers={"WWW-Authenticate": "Bearer"},
        )

async def get_current_user_from_token(token: str) -> UserOccupied:
    """
    从JWT Token中获取当前认证用户
    
    Args:
        token: JWT Token字符串
        
    Returns:
        返回当前认证的用户信息
        
    Raises:
        HTTPException: 如果用户未认证或字段缺失则抛出401错误
    """
    payload = verify_jwt_token(token)
    print(payload)
    user_id = payload.get("id")
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的Token",
            headers={"WWW-Authenticate": "Bearer"}
        )
    
    user = await User.get_or_none(id=user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户不存在",
            headers={"WWW-Authenticate": "Bearer"}
        )
    
    return UserOccupied(
        id=user.id,
        username=user.username,
        role=user.role
    )

async def get_current_user(request: Request) -> UserOccupied:
    """
    获取当前认证用户
    
    Args:
        request: FastAPI请求对象
        
    Returns:
        返回当前认证的用户信息
        
    Raises:
        HTTPException: 如果用户未认证或字段缺失则抛出401错误
    """
    user = request.state.user
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户未认证",
            headers={"WWW-Authenticate": "Bearer"}
        )
    
    # 检查必要字段是否存在
    if not all([user.id, user.username, user.role]):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户信息不完整",
            headers={"WWW-Authenticate": "Bearer"}
        )
    
    return UserOccupied(
        id=user.id,
        username=user.username,
        role=user.role
    )

# Basic Auth 认证
security = HTTPBasic()

async def authenticate_with_basic(credentials: HTTPBasicCredentials = Depends(security)):
    # 先判断是否是手机号格式（11位数字）
    if credentials.username.isdigit() and len(credentials.username) == 11:
        user = await User.get_or_none(phone=credentials.username)
    else:
        # 否则按邮箱处理
        user = await User.get_or_none(email=credentials.username)
    if not user or not user.verify_password(credentials.password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
            headers={"WWW-Authenticate": "Basic"},
        )
    return user

# 认证中间件
class AuthenticationMiddleware(BaseHTTPMiddleware):
    def __init__(self, app):
        super().__init__(app)
        self.excluded_paths = {
            "/docs",
            "/openapi.json",
            "/api/healthcheck",
            "/api/user/login",
            "/api/user/register"
        }

    async def dispatch(self, request: Request, call_next):
        # 检查请求路径是否在排除路径中
        if request.url.path in self.excluded_paths:
            return await call_next(request)
        
        # 尝试 JWT 认证
        auth_header = request.headers.get("Authorization")
        if auth_header and auth_header.startswith("Bearer "):
            token = auth_header.split(" ")[1]
            try:
                user = await get_current_user_from_token(token)
                request.state.user = user
                return await call_next(request)
            except HTTPException as e:
                return JSONResponse(
                    status_code=e.status_code,
                    content={"detail": e.detail}
                )
        
        # 尝试 Basic Auth 认证
        if AUTHENTICATE.get('BASIC_AUTH', False):
            try:
                credentials = await security(request)
                user = await authenticate_with_basic(credentials)
                request.state.user = UserOccupied(
                    id=user.id,
                    username=user.username,
                    role=user.role
                )
                return await call_next(request)
            except HTTPException as e:
                return JSONResponse(
                    status_code=e.status_code,
                    content={"detail": e.detail},
                    headers={"WWW-Authenticate": "Basic"}
                )
        
        # 如果两种认证都失败
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={"detail": "未提供有效的认证信息"},
            headers={"WWW-Authenticate": "Bearer"}        )
